package cn.xshi.gateway.route.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.xshi.gateway.util.GatewayRedisUtil;
import cn.xshi.gateway.util.GatewaySpringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cn.xshi.gateway.BaseResult;
import cn.xshi.gateway.route.client.vo.RequestInfo;
import cn.xshi.gateway.route.client.vo.RouteEntity;
import cn.xshi.gateway.route.model.DynamicRoute;
import cn.xshi.gateway.route.model.GatewayFilterDefinition;
import cn.xshi.gateway.route.model.GatewayPredicateDefinition;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
 * @Desc 动态路由设置
 * @Author 邓纯杰
 * @CreateTime 2012-12-12 12:12:12
 */
@Service
@Slf4j
public class DynamicRouteService implements ApplicationEventPublisherAware{

    private ApplicationEventPublisher applicationEventPublisher;

    public static final String JEHC_ROUTE = "JEHC:CLOUD:JEHC-ROUTE";// 网关动态路由Key

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     *
     */
    public void getRouteDefinitions(){
        RouteDefinitionLocator routeDefinitionLocator = GatewaySpringUtils.getBean(RouteDefinitionLocator.class);
        routeDefinitionLocator.getRouteDefinitions();
    }

    /**
     * 新增路由
     * @param routeDefinition
     * @return
     */
    public BaseResult addRouteDefinition(RouteDefinition routeDefinition) {
        try {
            RouteDefinitionWriter routeDefinitionWriter = GatewaySpringUtils.getBean(RouteDefinitionWriter.class);
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            return BaseResult.success();
        }catch (Exception e){
            log.error("创建路由失败,重新删除未成功的路由：",e);
            deleteRouteDefinition(routeDefinition.getId());
            return BaseResult.fail();
        }
    }

    /**
     * 更新路由
     * @param routeDefinition
     * @return
     */
    public BaseResult updateRouteDefinition(RouteDefinition routeDefinition) {
        try {
            RouteDefinitionWriter routeDefinitionWriter = GatewaySpringUtils.getBean(RouteDefinitionWriter.class);
            BaseResult result = deleteRouteDefinition(routeDefinition.getId());
            log.info("执行updateRouteDefinition方法时,删除网关路由，结果：{}",result);
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            return BaseResult.success();
        } catch (Exception e) {
            log.error("更新路由失败,重新删除未成功的路由：{}",e);
            deleteRouteDefinition(routeDefinition.getId());
            return BaseResult.fail("更新路由失败");
        }
    }

    /**
     * 删除路由
     * @param id
     * @return
     */
    public BaseResult deleteRouteDefinition(String id) {
        InMemoryRouteDefinitionRepository routeDefinitionWriter = GatewaySpringUtils.getBean(InMemoryRouteDefinitionRepository.class);
        try {
            routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            return BaseResult.success();
        }catch (Exception e){
            log.info("删除路由错误信息：{},路由编号：{}",e,id);
            return BaseResult.fail();
        }
    }

    /**
     *
     * @param requestInfo
     * @return
     */
    public BaseResult doRequestInfo(RequestInfo requestInfo,ApplicationEventPublisher publisher){
        if(null == applicationEventPublisher){
            applicationEventPublisher = publisher;
        }
        BaseResult baseResult = new BaseResult();
        if(null != requestInfo.getData()){
            GatewayRedisUtil gatewayRedisUtil = GatewaySpringUtils.getBean(GatewayRedisUtil.class);
            String json = JSON.toJSONString(requestInfo.getData());
            RouteEntity routeEntity = JSONObject.parseObject(json, RouteEntity.class);
            if(routeEntity.getMethod().equals("refresh")){
                log.info("开始刷新全部路由：{}",requestInfo);
                DynamicRouteEvent dynamicRouteEvent = GatewaySpringUtils.getBean(DynamicRouteEvent.class);
                dynamicRouteEvent.execute();
                log.info("执行刷新全部路由完毕：{}-{}",requestInfo);
            }
            if(null != routeEntity && !StringUtils.isEmpty(routeEntity.getId()) && !StringUtils.isEmpty(routeEntity.getMethod())){
                Object obj = ""+gatewayRedisUtil.hget(JEHC_ROUTE,routeEntity.getId());
                if(null != obj){
                    DynamicRoute dynamicRoute = JSON.parseObject(""+obj, DynamicRoute.class);
                    RouteDefinition routeDefinition = doRouteDefinition(dynamicRoute);
                    if(null != routeDefinition){
                        if(routeEntity.getMethod().equals("add")){
                            log.info("开始执行创建路由：{}",routeDefinition.getId());
                            baseResult = addRouteDefinition(routeDefinition);
                            log.info("执行创建路由完毕：{}",routeDefinition.getId());
                        }
                        if(routeEntity.getMethod().equals("update")){
                            log.info("开始执行更新路由：{}",routeDefinition.getId());
                            baseResult = updateRouteDefinition(routeDefinition);
                            log.info("执行更新路由完毕：{}",routeDefinition.getId());
                        }
                        if(routeEntity.getMethod().equals("delete")){
                            log.info("开始执行删除路由：{}",routeDefinition.getId());
                            baseResult = deleteRouteDefinition(dynamicRoute.getRouteId());
                            log.info("执行删除路由完毕：路由id：{}-{}",routeDefinition.getId(),baseResult);
                        }

                        if(baseResult.getSuccess()){
                            log.info("刷新全部路由成功，路由信息：{}",dynamicRoute);
                        }else{
                            log.info("刷新全部路由失败，路由信息：{}",dynamicRoute);
                        }
                    }
                }
            }
        }
        return baseResult;
    }

    /**
     * 转换RouteDefinition对象
     * @param dynamicRoute
     * @return
     */
    public synchronized RouteDefinition doRouteDefinition(DynamicRoute dynamicRoute) {

        //设置基础参数
        RouteDefinition definition = new RouteDefinition();
        definition.setId(dynamicRoute.getRouteId());
        definition.setOrder(dynamicRoute.getRouteOrder());

        //设置断言
        try {
            List<PredicateDefinition> pdList=new ArrayList<>();
            String predicates = dynamicRoute.getRoutePredicates();
            if(!StringUtils.isEmpty(predicates)){
                List<GatewayPredicateDefinition> gatewayPredicateDefinitions = JSON.parseArray(predicates, GatewayPredicateDefinition.class);
                if(!CollectionUtil.isEmpty(gatewayPredicateDefinitions)){
                    for (GatewayPredicateDefinition gatewayPredicateDefinition: gatewayPredicateDefinitions) {
                        PredicateDefinition predicate = new PredicateDefinition();
                        predicate.setArgs(gatewayPredicateDefinition.getArgs());
                        predicate.setName(gatewayPredicateDefinition.getName());
                        pdList.add(predicate);
                    }
                    definition.setPredicates(pdList);
                }
            }
        }catch (Exception e){
            log.error("转换断言时异常：{}",e);
        }

        //设置过滤器
        try {
            String filterStr = dynamicRoute.getRouteFilters();
            if(!StringUtils.isEmpty(filterStr)){
                List<GatewayFilterDefinition> gatewayFilterDefinitions = JSON.parseArray(filterStr, GatewayFilterDefinition.class);
                if(!CollectionUtil.isEmpty(gatewayFilterDefinitions)){
                    List<FilterDefinition> filters = new ArrayList();
                    for(GatewayFilterDefinition filterDefinition : gatewayFilterDefinitions){
                        FilterDefinition filter = new FilterDefinition();
                        filter.setName(filterDefinition.getName());
                        filter.setArgs(filterDefinition.getArgs());
                        filters.add(filter);
                    }
                    definition.setFilters(filters);
                }
            }
        }catch (Exception e){
            log.error("转换过滤器时异常：{}",e);
        }
        URI uri = null;
        if(dynamicRoute.getRouteUri().startsWith("http")){
            uri = UriComponentsBuilder.fromHttpUrl(dynamicRoute.getRouteUri()).build().toUri();
        }else{
            uri = URI.create(dynamicRoute.getRouteUri());
        }
        definition.setUri(uri);
        return definition;
    }
}
